use std::iter::Peekable;
use crate::error::{Error, Result};
use crate::sql::parse::ast::Column;
use crate::sql::parse::lexer::{Keyword, Lexer, Token};
use crate::sql::types::DataType;

pub mod lexer;
pub mod ast;

pub struct Parse<'a> {
    lexer: Peekable<Lexer<'a>>,
}

impl<'a> Parse<'a> {
    pub fn new(input: &'a str) -> Self {
        Parse {
            lexer: Lexer::new(input).peekable()
        }
    }

    //解析，获取抽象语法树
    pub fn parse(&mut self) -> Result<ast::Statement> {
        let stat = self.parse_statement()?;
        //期望 sql 语句的最后有个分号
        self.next_expect(Token::Semicolon)?;
        //分号之后不应该再有其他字符了
        if let Some(token) = self.peek()? {
            //如果还有其他字符，则不合法
            return Err(Error::Parse(format!("sql语句不合法，分号结尾之后还有其他字符：{}", token)));
        }
        Ok(stat)
    }

    //解析语句
    fn parse_statement(&mut self) -> Result<ast::Statement> {
        //第一个token类型
        match self.peek()? {
            //create语句
            Some(Token::Keyword(Keyword::Create)) => self.parse_ddl(),
            //select语句
            Some(Token::Keyword(Keyword::Select)) => self.parse_select(),
            //insert语句
            Some(Token::Keyword(Keyword::Insert)) => self.parse_insert(),


            Some(_t) => Err(Error::Parse(String::from("还没实现"))),
            None => Err(Error::Parse(String::from("语法错误"))),
        }
    }

    //解析ddl语句
    fn parse_ddl(&mut self) -> Result<ast::Statement> {
        match self.next()? {
            //若是create语句，则需要继续向后读取词法单元，后面的应该是create "table"
            Token::Keyword(Keyword::Create) => match self.next()? {
                //若是table关键字，则继续向后处理create table语句
                Token::Keyword(Keyword::Table) => self.parse_ddl_create_table(),
                _token => Err(Error::Parse(String::from("还没实现")))
            },
            _token => Err(Error::Parse(String::from("还没实现")))
        }
    }

    //解析select语句
    fn parse_select(&mut self) -> Result<ast::Statement> {
        //select
        self.next_expect(Token::Keyword(Keyword::Select))?;
        // *
        self.next_expect(Token::Asterisk)?;
        //from
        self.next_expect(Token::Keyword(Keyword::From))?;
        //表名
        let table_name = self.next_ident()?;

        Ok(ast::Statement::Select { table_name })
    }

    //解析insert语句
    fn parse_insert(&mut self) -> Result<ast::Statement> {
        //insert
        self.next_expect(Token::Keyword(Keyword::Insert))?;
        //into
        self.next_expect(Token::Keyword(Keyword::Into))?;
        //表名
        let table_name = self.next_ident()?;
        //表名之后可以是列名，如：insert into table_name (column1, column2, column3)
        //它是可选的，需要查看是否给指定的列进行insert
        let columns = if  self.next_if_token(Token::OpenParen).is_some() {
            //如果是括号，则解析列名
            let mut cols = Vec::new();
            loop {
                //列名添加到集合
                cols.push(self.next_ident()?.to_string());
                //列名之后可能还有其他的列名，也可能是括号闭合
                match self.next()? {
                    //括号闭合，说明列名解析完毕，退出循环
                    Token::CloseParen => break,
                    //逗号，说明之后还有列名需要解析
                    Token::Comma => continue,
                    token => return Err(Error::Parse(format!("解析Insert语句错误，列名编写有误：{}", token)))
                }
            }
            Some(cols)
        } else {
            //也可能是：insert into table_name VALUES (value1, value2, value3)
            //没有列名，返回None
            None
        };
        //解析values关键字
        self.next_expect(Token::Keyword(Keyword::Values))?;
        //解析要插入的数据，有两种形式：
        //1. insert into table values (v1, v2, v3);
        //2. insert into table values (v1, v2, v3), (v1, v2, v3), (v1, v2, v3);
        let mut values = Vec::new();
        loop {
            //左括号
            self.next_expect(Token::OpenParen)?;
            let mut exprs = Vec::new();
            loop {
                exprs.push(self.parse_expression()?);
                //列名之后可能还有其他的列名，也可能是括号闭合
                match self.next()? {
                    //括号闭合，说明列名解析完毕，退出循环
                    Token::CloseParen => break,
                    //逗号，说明之后还有列名需要解析
                    Token::Comma => continue,
                    token => return Err(Error::Parse(format!("解析Insert语句错误，插入的值编写有误：{}", token)))
                }
            }
            values.push(exprs);
            //如果没有逗号，退出循环，解析完毕
            if self.next_if_token(Token::Comma).is_none() {
                break
            }
        }
        Ok(ast::Statement::Insert {table_name, columns, values })
    }

    //解析 create table 语句
    fn parse_ddl_create_table(&mut self) -> Result<ast::Statement> {
        //create table 之后，应该跟上表名 create table sys_user，表名是标识符，
        //那么将向后消耗一个词法单元，并且下个词法单元必须是标识符
        let table_name = self.next_ident()?;
        //表名之后应该是括号
        self.next_expect(Token::OpenParen)?;
        //解析列信息
        let mut columns = Vec::new();
        loop {
            //将解析的列信息添加到集合
            columns.push(self.parse_ddl_column()?);
            //解析一个列后，可能是逗号。如：create table tb (column1, column2 column3)
            if self.next_if_token(Token::Comma).is_none() {
                //如果判断是否位逗号时，返回none，说明不是逗号，就跳出循环
                break;
            }
        }
        //括号闭合
        self.next_expect(Token::CloseParen)?;
        //返回抽象语法树
        Ok(ast::Statement::CreateTable {name: table_name, columns})
    }

    //解析列信息
    fn parse_ddl_column(&mut self) -> Result<ast::Column> {
        let mut column = Column {
            name: self.next_ident()?,
            datatype: match self.next()? {
                Token::Keyword(Keyword::Int) | Token::Keyword(Keyword::Integer) => DataType::Integer,
                Token::Keyword(Keyword::Bool) | Token::Keyword(Keyword::Boolean) => DataType::Boolean,
                Token::Keyword(Keyword::Float) | Token::Keyword(Keyword::Double) => DataType::Float,
                Token::Keyword(Keyword::String) | Token::Keyword(Keyword::Text) | Token::Keyword(Keyword::Varchar) => DataType::String,
                token => return Err(Error::Parse(format!("解析数据类型错误，非法类型：{}", token)))
            },
            nullable: None, //是否可以为空
            default: None,  //默认值
        };

        //检查列是否有默认值，是否可以为空
        while let Some(Token::Keyword(keyword)) = self.next_if_keyword() {
            match keyword {
                //Null说明允许为空
                Keyword::Null => column.nullable = Some(true),
                //Not说明不允许为空，那么下一个词法单元必须是Null，这样才能组合成Not Null
                Keyword::Not => {
                    self.next_expect(Token::Keyword(Keyword::Null))?;
                    //若匹配了Not Null，则这个字段不允许为空
                    column.nullable = Some(false);
                },
                Keyword::Default => column.default = Some(self.parse_expression()?),
                k => return Err(Error::Parse(format!("解析Not Null发生错误，非法关键字：{}", k)))
            }
        }
        Ok(column)
    }

    //解析表达式
    fn parse_expression(&mut self) -> Result<ast::Expression> {
        Ok(match self.next()? {
            //如果下一个词法单元是数值
            Token::Number(n) => {
                if n.chars().all(|c| c.is_ascii_digit()) {
                    //整数 - 全是数字不包含小数点
                    ast::Consts::Integer(n.parse()?).into()
                } else {
                    //浮点数 - 包含小数点
                    ast::Consts::Float(n.parse()?).into()
                }
            },
            //如果下一个词法单元是字符串
            Token::String(s) => ast::Consts::String(s).into(),
            Token::Keyword(Keyword::True) => ast::Consts::Boolean(true).into(),
            Token::Keyword(Keyword::False) => ast::Consts::Boolean(false).into(),
            Token::Keyword(Keyword::Null) => ast::Consts::Null.into(),
            t => return Err(Error::Parse(format!("解析表达式错误：{}", t)))
        })
    }

    //向前窥探一个词法单元
    fn peek(&mut self) -> Result<Option<Token>> {
        //克隆，并将Option返回结果转为Result
        self.lexer.peek().cloned().transpose()
    }

    //消耗一个词法单元
    fn next(&mut self) -> Result<Token> {
        self.lexer.next().unwrap_or_else(|| Err(Error::Parse(String::from("语法错误"))))
    }

    //消耗一个词法单元，但消耗的这个词法单元，必须是标识符
    fn next_ident(&mut self) -> Result<String> {
        match self.next()? {
            Token::Ident(ident) => Ok(ident),
            token => Err(Error::Parse(format!("期望是标识符，但却不是，所以报错了：{}", token)))
        }
    }

    //下一个词法单元必须是指定的
    fn next_expect(&mut self, expect: Token) -> Result<()> {
        let token = self.next()?;
        if token != expect {
            return Err(Error::Parse(format!("期望是：{}，但实际是：{}", expect, token)));
        }
        Ok(())
    }

    //如果是满足条件，跳转到下一个Token
    fn next_if<F: Fn(&Token) -> bool>(&mut self, predicate: F) -> Option<Token> {
        self.peek().unwrap_or(None).filter(|t| predicate(t))?;
        self.next().ok()
    }

    //如果是关键字，进行跳转并返回
    fn next_if_keyword(&mut self) -> Option<Token> {
        self.next_if(|t| matches!(t, Token::Keyword(_)))
    }

    fn next_if_token(&mut self, token: Token) -> Option<Token> {
        self.next_if(|t| t == &token)
    }

}

#[cfg(test)]
mod tests {
    use crate::error::Result;
    use crate::sql::parse::Parse;

    #[test]
    fn test_parse_create_table() -> Result<()> {
        let sql = "
            create table table1 (
                a int default 100,
                b float not null,
                c varchar null,
                d bool default true
            );
        ";
        let ast = Parse::new(sql).parse()?;
        println!("{:?}", ast);
        Ok(())
    }

    #[test]
    fn test_parse_insert() -> Result<()> {
        let sql = "
            insert into tab1 values (1, 2, 3, 'a', true);
        ";
        let ast = Parse::new(sql).parse()?;
        println!("{:?}", ast);


        let sql2 = "
            insert into tab1 (col1, col2, col3, col4, col5) values (1, 2, 3, 'a', true);
        ";
        let ast2 = Parse::new(sql2).parse()?;
        println!("{:?}", ast2);


        let sql3 = "
            insert into tab1 (col1, col2, col3, col4, col5) values (1, 2, 3, 'a', true), (4, 5, 6, 'b', false);
        ";
        let ast3 = Parse::new(sql3).parse()?;
        println!("{:?}", ast3);


        Ok(())
    }

    #[test]
    fn test_parse_select() -> Result<()> {
        let sql = "
            select * from tab1;
        ";
        let ast = Parse::new(sql).parse()?;
        println!("{:?}", ast);

        Ok(())
    }

}
